/*
 * SSD1289.c
 *
 * Created: 2012-05-28 22:57:40
 *  Author: Sebastian_2
 */ 

#include <util/delay.h>
#include "../conf.h"
#include "../Globals.h"
#include "SSD1289.h"

// IO functions
void SSD1289_Init() {
#ifndef ATMEGA
	SSD1289_DATA_LO.DIR = 0xFF; // all output
	SSD1289_DATA_HI.DIR = 0xFF; // all output
	SSD1289_CONFIG.DIR = 0xFF; // all output
#else
	DATA_DDR = 0xFF;
	RESET_DDR |= (1 << RESET_BIT);
	RESET_SET();
	CS_DDR |= (1 << CS_BIT);
	CS_SET();
	RD_DDR |= (1 << RD_BIT);
	RD_SET();
	WR_DDR |= (1 << WR_BIT);
	WR_SET();
	RS_DDR |= (1 << RS_BIT);	
	RS_SET();
	LATCH_DDR |= (1 << LATCH_BIT);
	LATCH_CLR();
	LED_DDR |= (1 << LED_BIT);
	LED_CLR();
	CS_SET();
	RS_SET();
	RD_SET();
	CS_SET();
#endif

	SSD1289_WriteRegister(0x00, 0x0001);
	_delay_ms(15);

	SSD1289_WriteRegister(0x03, 0xAAAC);
	SSD1289_WriteRegister(0x0C, 0x0002);
	SSD1289_WriteRegister(0x0D, 0x000A);
	SSD1289_WriteRegister(0x0E, 0x2C00);
	SSD1289_WriteRegister(0x1E, 0x00B8);
	_delay_ms(15);

	SSD1289_WriteRegister(0x01, 0x2B3F);
	SSD1289_WriteRegister(0x02, 0x0600);
	SSD1289_WriteRegister(0x10, 0x0000);
	SSD1289_WriteRegister(0x11, 0x6018);
	SSD1289_WriteRegister(0x07, 0x0233);
	SSD1289_WriteRegister(0x0B, 0x5312);
	SSD1289_WriteRegister(0x0F, 0x0000);
	_delay_ms(15);

	SSD1289_WriteRegister(0x48, 0x0000);
	SSD1289_WriteRegister(0x49, 0x013F);
	SSD1289_WriteRegister(0x44, 0xEF00);
	SSD1289_WriteRegister(0x45, 0x0000);
	SSD1289_WriteRegister(0x46, 0x013F);
}

void SSD1289_WriteCommand(uint8_t c) {
	CS_CLR();
	RS_CLR();
#ifndef ATMEGA
	SEND_LO(c);
	SEND_HI(0x00);
#else
	SEND(c >> 8);
	LATCH_SET();
	LATCH_CLR();
	SEND(c);
#endif	
	WR_CLR();
	WR_SET();
	
	RS_SET();
	CS_SET();
}

void SSD1289_WriteData(uint16_t d) {
	CS_CLR();
#ifndef ATMEGA
	SEND_HI(d >> 8);
	SEND_LO(d);
#else
	SEND(d >> 8);
	LATCH_SET();
	LATCH_CLR();
	SEND(d);
#endif
	WR_CLR();
	WR_SET();

	CS_SET();
}

void SSD1289_WriteRegister(uint8_t c, uint16_t d) {
	SSD1289_WriteCommand(c);
	SSD1289_WriteData(d);
}

// helper functions

void SSD1289_SetCursor(point *p) {
	SSD1289_WriteRegister(0x4E, p->y);
	SSD1289_WriteRegister(0x4F, SSD1289_MaxX - p->x);
	SSD1289_WriteCommand(0x22);
}

void SSD1289_SetPaintArea(point *start, point *stop) {
	SSD1289_WriteRegister(0x44, (stop->y << 8) | start->y);
	SSD1289_WriteRegister(0x45, SSD1289_MaxX - stop->x);
	SSD1289_WriteRegister(0x46, SSD1289_MaxX - start->x);

	SSD1289_SetCursor(start);
}

// rectangle drawing functions

void SSD1289_DrawRect(point *start, point *stop) {
	SSD1289_SetPaintArea(start, stop);
	
	uint16_t x;
	uint8_t y;

	for(y = start->y; y <= stop->y; y++)
	{
		for(x = start->x; x <= stop->x; x++)
		{
			SSD1289_WriteData(gColour);
		}
	}
}

void SSD1289_DrawLineX(point *start, point *stop, uint8_t width) {
	gP1 = IncY(stop, width);
	SSD1289_DrawRect(start, &gP1);
}

void SSD1289_DrawLineY(point *start, point *stop, uint8_t width) {
	gP1 = IncX(stop, width);
	SSD1289_DrawRect(start, &gP1);
}

void SSD1289_Clear() {
	uint16_t tmp = gColour;
	gColour = BACKGROUND;
	point p1 = {0, 0};
	point p2 = {SSD1289_MaxX, SSD1289_MaxY};
	SSD1289_DrawRect(&p1, &p2);
	gColour = tmp;
}

// text drawing

void SSD1289_DrawPixel(point *location) {
	SSD1289_SetCursor(location);
	SSD1289_WriteData(gColour);
}

point SSD1289_DrawChar(char c, point *location, const FONT_INFO *font) {
	char ch = c - pgm_read_byte(&(font->startChar));
	gP1 = *location;

	SSD1289_SetCursor(location);
	uint8_t height = pgm_read_byte(&(font->heightPixels));
	uint8_t space = pgm_read_byte(&(font->spacePixels));
	if(c == '\n') {
		return IncY(&gP1, height);
	}
	
	if(c == ' ') {
		return IncX(&gP1, 2*space);
	}
	
	FONT_CHAR_INFO *fChar_ptr = (FONT_CHAR_INFO *)pgm_read_word(&(font->charInfo));
	FONT_CHAR_INFO *fChar = (FONT_CHAR_INFO *)(fChar_ptr + ch);
	uint8_t *fBitmap = (uint8_t*)pgm_read_word(&(font->data));
	uint8_t width = pgm_read_byte(&(fChar->widthBits));
	uint16_t offset = pgm_read_word(&(fChar->offset));
	uint16_t temp = (uint16_t)fBitmap + offset;
	
	uint8_t rounds = width/8;
	if(rounds*8 < width)
		rounds++;
	
	uint8_t mask, byte;
		
	for(uint8_t i = 0; i < height; i++) {
		mask = 0x80;
		if(i > 0) {
			gP1 = IncY(&gP1, 1);
			gP1 = DecX(&gP1, 8*rounds);
		}
		for(uint8_t n = 0; n < rounds; n++) {
			byte = pgm_read_byte(temp);
			temp++;
			mask = 0x80;
			for(uint8_t k = 0; k < 8; k++) {
				if(byte & mask) {
					mask >>= 1;
					gP1 = IncX(&gP1, 1);
					SSD1289_DrawPixel(&gP1);
				} else {
					mask >>= 1;
					gP1 = IncX(&gP1, 1);
				}
			}
		}
	}	
	return DecY(&gP1, height-1);
}

point SSD1289_PrintString(char *c, point *location, const FONT_INFO *font) {
	char ch = 0;
	uint16_t x = location->x;
	gP1 = *location;
	while((ch = *c++))
	{
		if(ch == (char)0) { // end of string
			ch = *c;
		} else if (ch == (char)'\n') { // jump to next line
			gP1.x = x;
			gP1 = SSD1289_DrawChar(ch, &gP1, font);
		} else {
			gP1 = SSD1289_DrawChar(ch, &gP1, font);
		}
	}
	return gP1;
}